package fjut.acm.coding.controller.judge;

import com.alibaba.fastjson.JSONObject;
import fjut.acm.coding.component.judge.JudgeHttpClient;
import fjut.acm.coding.component.judge.data.JudgeResultData;
import fjut.acm.coding.component.judge.data.JudgeSubmitData;
import fjut.acm.coding.component.judge.data.LanguageType;
import fjut.acm.coding.component.judge.data.StatusType;
import fjut.acm.coding.config.interceptor.PrivateRequired.PrivateRequired;
import fjut.acm.coding.entity.JudgeStatus;
import fjut.acm.coding.entity.ProblemInfo;
import fjut.acm.coding.entity.UserContestProblemSloved;
import fjut.acm.coding.entity.result.ResultCode;
import fjut.acm.coding.entity.result.ResultJson;
import fjut.acm.coding.service.JudgeStatusService;
import fjut.acm.coding.service.ProblemInfoService;
import fjut.acm.coding.service.UserContestProblemSlovedService;
import fjut.acm.coding.service.UserCustomInfoService;
import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import java.util.Date;

@RestController
@CrossOrigin
@RequestMapping("/judge_status")
public class JudgeController {

    @Resource
    private JudgeHttpClient judgeHttpClient;


    @Resource
    private JudgeStatusService judgeStatusService;

    @Resource
    private ProblemInfoService problemInfoService;

    @Resource
    private UserContestProblemSlovedService userContestProblemSlovedService;

    @Resource
    private UserCustomInfoService userCustomInfoService;


    /**
     *  接收测评结果的接口
     *
     * @param rid   本地测评的id
     * @param problem  本地题目的id
     * @param result  测评结果
     * @param resultInfo  测评的结果信息
     * @param memory  空间占有
     * @param time   时间占有
     * @param language  语言
     * @param codeLength  代码长度
     */
    @PostMapping("/getJudgeResult")
    public void getJudgeResult(@RequestParam("rid") String rid,
                               @RequestParam("problem") String problem,
                               @RequestParam("result") String result,
                               @RequestParam("resultInfo") String resultInfo,
                               @RequestParam("memory") String memory,
                               @RequestParam("time") String time,
                               @RequestParam("language") String language,
                               @RequestParam("codeLength") String codeLength){
        // 根据rid获取contestId
        JudgeStatus judgeStatus = judgeStatusService.selectJudgeById(Integer.valueOf(rid));
        Integer contestId = judgeStatus.getContestId();
        if(contestId != -1){
            String username = judgeStatus.getUsername();
            UserContestProblemSloved userContestProblemSloved = userContestProblemSlovedService.selectOnlyOne(username,contestId,judgeStatus.getProblemId());
            if(userContestProblemSloved == null){
                userContestProblemSloved = new UserContestProblemSloved();
                userContestProblemSloved.setContestId(contestId);
                userContestProblemSloved.setProblemId(judgeStatus.getProblemId());
                userContestProblemSloved.setUsername(username);
                userContestProblemSloved.setSloved(0);
                userContestProblemSloved.setWrong(0);
                userContestProblemSloved.setNickName(userCustomInfoService.select(username).getNickname());
                userContestProblemSlovedService.addOneUserContestProblemSloved(userContestProblemSloved);
            }

            // 题目通过，sloved置1
            if(result.equals(StatusType.AC.getName())){
                userContestProblemSloved.setSloved(1);
                userContestProblemSlovedService.updateSloved(userContestProblemSloved);
            }
            //没有通过，这个题目的错误次数+1
            else{
                userContestProblemSloved.setWrong(userContestProblemSloved.getWrong()+1);
                userContestProblemSlovedService.updateWrong(userContestProblemSloved);
            }
        }

        JudgeResultData judgeResultData = new JudgeResultData();
        judgeResultData.setRid(rid);
        judgeResultData.setProblem(problem);
        judgeResultData.setResult(result);
        judgeResultData.setResultInfo(resultInfo);
        judgeResultData.setMemory(memory);
        judgeResultData.setTime(time);
        judgeResultData.setLanguage(language);
        judgeResultData.setCodeLength(codeLength);
        judgeStatusService.handleJudgeResult(judgeResultData);
    }


    /**
     *
     * 屏蔽了后端对 远程/本地测评 的感知，只需要将测评信息提交到测评机就行
     *
     * @param problemId
     * @param code
     * @param languageStr
     * @param username
     * @param contestId
     * @return
     */
    @PrivateRequired
    @PostMapping("/submit")
    public ResultJson submit(@RequestParam("problemId") Integer problemId,
                             @RequestParam("code") String code,
                             @RequestParam("language") String languageStr,
                             @RequestParam("username") String username,
                             @RequestParam(value = "contestId", required = false) Integer contestId) throws Exception {
        ResultJson resultJson = new ResultJson();

        // 语言类型字符串转化为对应的码
        int language = LanguageType.getCodeByName(languageStr);

        if(language == -1){
            resultJson.setStatus(ResultCode.BAD_REQUEST,"还未支持该语言的测评！");
            return resultJson;
        }

        // 插入一条测评记录，获取相应的runId
        Date currentTime = new Date();
        JudgeStatus judgeStatus = new JudgeStatus();
        judgeStatus.setUsername(username);
        judgeStatus.setProblemId(problemId);
        judgeStatus.setContestId(contestId);
        judgeStatus.setLanguage(language);
        judgeStatus.setSubmitTime(currentTime);
        judgeStatus.setResult(StatusType.PENDING.getCode());
        judgeStatus.setTimeUsed("-");
        judgeStatus.setMemoryUsed("-");
        judgeStatus.setCode(code);
        judgeStatus.setCodeLength(code.replaceAll("\\s*", "").length());
        judgeStatusService.insert(judgeStatus);
        Integer runId = judgeStatus.getId();


        // 根据 problemId 获取belong_oj_id 和 belong_problem_id
        // 当 belong_oj_id为0的时候，说明是本地测评，则problemId 和 belong_problem_id 相同
        ProblemInfo problemInfo = problemInfoService.selectProblemInfo(problemId);

        // 生成测评需要的数据
        JudgeSubmitData judgeData = new JudgeSubmitData();
        judgeData.setRid(runId.toString());
        judgeData.setOjId(problemInfo.getBelongOjId());
        judgeData.setPid(problemInfo.getBelongProblemId());
        judgeData.setLanguage(String.valueOf(language));
        // 远程测评的时候，这两个属性是没用的。本地测评的时候，这两个属性才会生效，暂时定为死值，测评机还在完善中。
        judgeData.setTime(2000);
        judgeData.setMemory(65536);
        // code经过base64编码之后传递，解决了转义符在传输过程中被二次转义的问题
        judgeData.setCode(new BASE64Encoder().encode(code.getBytes()));

        // 将代码提交到测评机
        JSONObject jsonObject;
        try{
            jsonObject = judgeHttpClient.submit(judgeData);
        }catch(Exception e){
            // 请求评测机出现异常，返回失败状态
            judgeStatusService.ifLocalJudgeError(judgeStatus);
            resultJson.setStatus(ResultCode.BUSINESS_FAIL, "提交代码到远端评测机失败！评测机连接异常");
            return resultJson;
        }

        // 代码提交到测评机成功，测评机状态返回正常，测评机器测评中
        String submitResult = jsonObject.getString("msg");
        if ("success".equals(submitResult)) {
            // 更新数据库表
            judgeStatusService.ifSubmitSuccess(judgeStatus);
            resultJson.setStatus(ResultCode.REQUIRED_SUCCESS, "提交评测成功！");
        }
        else if("failed".equals(submitResult)){
            judgeStatusService.ifSubmitError(judgeStatus);
            resultJson.setStatus(ResultCode.BUSINESS_FAIL, "本地测评机不支持该语言测评");
        }
        // 测评机返回状态异常
        else{
            // 更新数据库表，返回结果
            judgeStatusService.ifSubmitError(judgeStatus);
            resultJson.setStatus(ResultCode.BUSINESS_FAIL, "提交代码到本地评测机失败！评测机内部异常");
        }
        return resultJson;
    }
}
